package de.bk.timecalc.gui;

import static de.bk.timecalc.gui.GuiToolbox.*;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

import de.bk.timecalc.gui.GuiToolbox.DefaultSelectionListener;

/**
 * Creates the gui components.
 * 
 * @author Bastian Krol *
 */
public class GameWindowLayout
{
  static final String SHELL_ICON_IMAGE = "ql.png";
  private static final String BACKGROUND_IMAGE = "background_ql_smoke.png";

  private static final int HEADER_BIG_CLOCK_HEIGHT = 38;

  private static final int MAIN_AREA_WIDTH = 118;
  private static final int MAIN_AREA_HEIGHT = 250;
  private static final int MAIN_AREA_CELL_HORIZONTAL_INDENT = 22;
  private static final int MAIN_AREA_CELL_WIDTH = 20;
  private static final int MAIN_AREA_COLON_WIDTH = 6;
  private static final int MAIN_AREA_CELL_HEIGHT = 18;

  private static final int STAT_AREA_WIDTH = 180;
  private static final int STAT_LABEL_WIDTH = 70;
  private static final int STAT_LABEL_HEIGHT = 18;
  private static final int STAT_VALUE_WIDTH = 90;
  private static final int STAT_VALUE_HEIGHT = 18;

  private static final int FOOTER_BUTTON_BAR_WIDTH = MAIN_AREA_WIDTH
      + STAT_AREA_WIDTH;
  private static final int FOOTER_BUTTON_BAR_HEIGHT = 30;

  private static final int SEPARATOR_DIMENSION = 5;

  private static final int SHELL_WIDTH = MAIN_AREA_WIDTH + SEPARATOR_DIMENSION
      + STAT_AREA_WIDTH + 40;
  private static final int SHELL_HEIGHT = HEADER_BIG_CLOCK_HEIGHT
      + MAIN_AREA_HEIGHT + SEPARATOR_DIMENSION + FOOTER_BUTTON_BAR_HEIGHT + 90;

  private GameWindowBehaviour behaviourDelegate;
  private Shell shell;

  private Font fontBigClock;

  // header
  Text textBigClock;

  // main area
  Composite compositeMainArea;
  Text textItemTakenAt;
  Text textClockTakenAtMinute;
  Text textClockTakenAtSecond;
  Text textItemWillRespawnAt;
  Text textClockWillRespawnAtMinute;
  Text textClockWillRespawnAtSecond;

  // text fields for stats
  Text textStatLastAnswerRightWrong;
  Text textStatLastChallengeSolution;
  Text textStatTimeNeededForLastChallenge;
  Text textStatScoreForLastChallenge;
  Text textStatOverallScore;
  Text textStatAverageScore;

  // footer button bar
  Button buttonPause;
  Button buttonReset;

  GameWindowLayout(GameWindowBehaviour behaviourDelegate)
  {
    this.behaviourDelegate = behaviourDelegate;
  }

  void initShell(Display display)
  {
    this.createFonts(display);
    this.createShell(display);
    this.createHeaderBigClock(this.shell);
    this.createMainArea(this.shell);
    this.createStatsArea(this.shell);
    this.createFooterButtonBarArea(this.shell);
    this.shell.pack();
  }

  Shell getShell()
  {
    return this.shell;
  }

  void openShell()
  {
    this.shell.open();
  }

  private void createFonts(Display display)
  {
    Font systemFont = display.getSystemFont();
    String systemFontName = systemFont.getFontData()[0].getName();
    int systemFontHeight = systemFont.getFontData()[0].getHeight();
    FontData fontData = new FontData(systemFontName,
        (int) ((float) systemFontHeight * 1.8f), SWT.BOLD);
    this.fontBigClock = new Font(display, fontData);
  }

  private void createShell(Display display)
  {
    this.shell = new Shell(display, SWT.CLOSE | SWT.TITLE | SWT.MIN | SWT.MAX
        | SWT.RESIZE);
    this.shell.setImage(new Image(display, TimeCalcGui.class
        .getResourceAsStream(SHELL_ICON_IMAGE)));
    this.shell.setBackgroundImage(new Image(display, TimeCalcGui.class
        .getResourceAsStream(BACKGROUND_IMAGE)));
    this.shell.setBackgroundMode(SWT.INHERIT_FORCE);
    this.shell.setText("QL Time Calc Trainer");
    this.shell.setSize(SHELL_WIDTH, SHELL_HEIGHT);
    GridLayout layoutShell = new GridLayout(2, false);
    this.shell.setLayout(layoutShell);
  }

  private void createHeaderBigClock(Composite root)
  {
    Composite compositeHeader = new Composite(root, SWT.BORDER);
    GridData gridDataHeader = new GridData();
    gridDataHeader.horizontalAlignment = SWT.CENTER;
    gridDataHeader.verticalAlignment = SWT.CENTER;
    gridDataHeader.horizontalSpan = 2;
    gridDataHeader.widthHint = MAIN_AREA_WIDTH + STAT_AREA_WIDTH;
    gridDataHeader.heightHint = HEADER_BIG_CLOCK_HEIGHT;
    compositeHeader.setLayoutData(gridDataHeader);

    GridLayout layoutHeader = new GridLayout(1, false);
    compositeHeader.setLayout(layoutHeader);

    this.textBigClock = new Text(compositeHeader, SWT.SINGLE | SWT.CENTER);
    this.textBigClock.setFont(this.fontBigClock);
    this.textBigClock.setEditable(false);
    this.textBigClock.setText("13:24");
    GridData gridDataBigClock = new GridData();
    gridDataBigClock.horizontalAlignment = SWT.CENTER;
    gridDataBigClock.verticalAlignment = SWT.CENTER;
    gridDataBigClock.grabExcessHorizontalSpace = true;
    gridDataBigClock.grabExcessVerticalSpace = true;
    this.textBigClock.setLayoutData(gridDataBigClock);
  }

  private void createMainArea(Composite root)
  {
    this.compositeMainArea = new Composite(root, SWT.BORDER);

    GridData gridDataMainArea = new GridData();
    gridDataMainArea.horizontalAlignment = SWT.RIGHT;
    gridDataMainArea.verticalAlignment = SWT.CENTER;
    gridDataMainArea.grabExcessHorizontalSpace = true;
    gridDataMainArea.grabExcessVerticalSpace = true;
    gridDataMainArea.widthHint = MAIN_AREA_WIDTH;
    gridDataMainArea.heightHint = MAIN_AREA_HEIGHT;
    this.compositeMainArea.setLayoutData(gridDataMainArea);

    GridLayout layoutMain = new GridLayout(3, false);
    this.compositeMainArea.setLayout(layoutMain);

    GridData gridDataClockLeft = new GridData();
    gridDataClockLeft.horizontalAlignment = SWT.RIGHT;
    gridDataClockLeft.verticalAlignment = SWT.CENTER;
    gridDataClockLeft.widthHint = MAIN_AREA_CELL_WIDTH;
    gridDataClockLeft.heightHint = MAIN_AREA_CELL_HEIGHT;
    gridDataClockLeft.horizontalIndent = MAIN_AREA_CELL_HORIZONTAL_INDENT;
    GridData gridDataClockCenter = new GridData();
    gridDataClockCenter.horizontalAlignment = SWT.CENTER;
    gridDataClockCenter.verticalAlignment = SWT.CENTER;
    gridDataClockCenter.widthHint = MAIN_AREA_COLON_WIDTH;
    gridDataClockCenter.heightHint = MAIN_AREA_CELL_HEIGHT;
    GridData gridDataClockRight = new GridData();
    gridDataClockRight.horizontalAlignment = SWT.LEFT;
    gridDataClockRight.verticalAlignment = SWT.CENTER;
    gridDataClockRight.widthHint = MAIN_AREA_CELL_WIDTH;
    gridDataClockRight.heightHint = MAIN_AREA_CELL_HEIGHT;
    GridData gridDataSpanThreeColumns = new GridData();
    gridDataSpanThreeColumns.horizontalSpan = 3;
    gridDataSpanThreeColumns.horizontalAlignment = SWT.CENTER;
    gridDataSpanThreeColumns.verticalAlignment = SWT.CENTER;
    gridDataSpanThreeColumns.widthHint = 2 * MAIN_AREA_CELL_HORIZONTAL_INDENT
        + 2 * MAIN_AREA_CELL_WIDTH + MAIN_AREA_COLON_WIDTH;
    gridDataSpanThreeColumns.heightHint = MAIN_AREA_CELL_HEIGHT;

    this.textItemTakenAt = createUneditableText(this.compositeMainArea,
        gridDataSpanThreeColumns);
    createLabel(this.compositeMainArea, gridDataSpanThreeColumns, "taken at");
    this.textClockTakenAtMinute = createUneditableText(this.compositeMainArea,
        gridDataClockLeft);
    createLabel(this.compositeMainArea, gridDataClockCenter, ":");
    this.textClockTakenAtSecond = createUneditableText(this.compositeMainArea,
        gridDataClockRight);

    createHorizontalSeparator(this.compositeMainArea, gridDataSpanThreeColumns);

    this.textItemWillRespawnAt = createUneditableText(this.compositeMainArea,
        gridDataSpanThreeColumns);
    createLabel(this.compositeMainArea, gridDataSpanThreeColumns,
        "will respawn at");
    this.textClockWillRespawnAtMinute = this.createEditableTextClockPart(
        this.compositeMainArea, gridDataClockLeft);
    createLabel(this.compositeMainArea, gridDataClockCenter, ":");
    this.textClockWillRespawnAtSecond = this.createEditableTextClockPart(
        this.compositeMainArea, gridDataClockRight);

    createHorizontalSeparator(this.compositeMainArea, gridDataSpanThreeColumns);

    GridData gridDataButtonEnter = new GridData();
    gridDataButtonEnter.horizontalSpan = 3;
    gridDataButtonEnter.horizontalAlignment = SWT.CENTER;
    gridDataButtonEnter.widthHint = 100;
    gridDataButtonEnter.heightHint = 50;
    Button buttonEnter = new Button(this.compositeMainArea, SWT.PUSH);
    buttonEnter.setText("Enter");
    buttonEnter.setLayoutData(gridDataButtonEnter);
    buttonEnter.addSelectionListener(new DefaultSelectionListener()
    {
      @Override
      public void widgetSelected(SelectionEvent e)
      {
        GameWindowLayout.this.behaviourDelegate
            .checkAnswerAndStartNextChallenge();
      }
    });

    this.addNextWidget(this.textClockWillRespawnAtMinute,
        this.textClockWillRespawnAtSecond);
    this.addNextWidget(this.textClockWillRespawnAtSecond, buttonEnter);
  }

  private void createStatsArea(Composite root)
  {
    Composite compositeStatsArea = new Composite(root, SWT.BORDER);
    GridData gridDataStatsArea = new GridData();
    gridDataStatsArea.horizontalAlignment = SWT.LEFT;
    gridDataStatsArea.verticalAlignment = SWT.CENTER;
    gridDataStatsArea.grabExcessHorizontalSpace = true;
    gridDataStatsArea.grabExcessVerticalSpace = true;
    gridDataStatsArea.widthHint = STAT_AREA_WIDTH;
    gridDataStatsArea.heightHint = MAIN_AREA_HEIGHT;
    compositeStatsArea.setLayoutData(gridDataStatsArea);

    GridLayout layoutStats = new GridLayout(2, false);
    compositeStatsArea.setLayout(layoutStats);

    GridData gridDataLabel = new GridData();
    gridDataLabel.horizontalAlignment = SWT.LEFT;
    gridDataLabel.verticalAlignment = SWT.CENTER;
    gridDataLabel.widthHint = STAT_LABEL_WIDTH;
    gridDataLabel.heightHint = STAT_LABEL_HEIGHT;
    GridData gridDataValue = new GridData();
    gridDataValue.horizontalAlignment = SWT.LEFT;
    gridDataValue.verticalAlignment = SWT.CENTER;
    gridDataValue.widthHint = STAT_VALUE_WIDTH;
    gridDataValue.heightHint = STAT_VALUE_HEIGHT;

    this.textStatLastAnswerRightWrong = GuiToolbox.createLabelAndField(
        compositeStatsArea, "Last answer",
        "Shows if the last answer you gave was correct or not", gridDataLabel,
        gridDataValue);
    this.textStatLastChallengeSolution = GuiToolbox.createLabelAndField(
        compositeStatsArea, "Last solution",
        "Shows the solution for the last question", gridDataLabel,
        gridDataValue);
    this.textStatTimeNeededForLastChallenge = GuiToolbox.createLabelAndField(
        compositeStatsArea, "Time needed",
        "Shows the time it took you to enter the last solution", gridDataLabel,
        gridDataValue);
    this.textStatScoreForLastChallenge = GuiToolbox.createLabelAndField(
        compositeStatsArea, "Score last",
        "Shows the score for your last answer", gridDataLabel, gridDataValue);
    this.textStatOverallScore = GuiToolbox.createLabelAndField(
        compositeStatsArea, "Total Score",
        "Shows the total score for all answers so far", gridDataLabel,
        gridDataValue);
    this.textStatAverageScore = GuiToolbox
        .createLabelAndField(
            compositeStatsArea,
            "Avg. Score",
            "Shows the average score for all answers so far, that is, your total score divided by the number of answers",
            gridDataLabel, gridDataValue);
  }

  private void createFooterButtonBarArea(Composite root)
  {
    Composite compositeFooterButtonBarArea = new Composite(root, SWT.NONE);
    GridData gridDataFooterButtonBarArea = new GridData();
    gridDataFooterButtonBarArea.horizontalSpan = 2;
    gridDataFooterButtonBarArea.horizontalAlignment = SWT.CENTER;
    gridDataFooterButtonBarArea.verticalAlignment = SWT.CENTER;
    gridDataFooterButtonBarArea.widthHint = FOOTER_BUTTON_BAR_WIDTH;
    gridDataFooterButtonBarArea.heightHint = FOOTER_BUTTON_BAR_HEIGHT;
    gridDataFooterButtonBarArea.grabExcessHorizontalSpace = true;
    gridDataFooterButtonBarArea.grabExcessVerticalSpace = true;
    compositeFooterButtonBarArea.setLayoutData(gridDataFooterButtonBarArea);
    FillLayout layoutFooterButtonBarArea = new FillLayout(SWT.HORIZONTAL);
    compositeFooterButtonBarArea.setLayout(layoutFooterButtonBarArea);
    this.buttonReset = GuiToolbox.createButton(compositeFooterButtonBarArea,
        "Reset");
    this.buttonReset.addSelectionListener(new DefaultSelectionListener()
    {
      @Override
      public void widgetSelected(SelectionEvent e)
      {
        GameWindowLayout.this.behaviourDelegate.reset();
      }
    });
    this.buttonPause = GuiToolbox.createButton(compositeFooterButtonBarArea,
        "Pause");
    this.buttonPause.addSelectionListener(new DefaultSelectionListener()
    {
      @Override
      public void widgetSelected(SelectionEvent e)
      {
        GameWindowLayout.this.togglePauseFromButton();
      }
    });

  }

  private Text createEditableTextClockPart(Composite composite,
      GridData gridData)
  {
    final Text text = createText(composite, gridData);
    text.setTextLimit(2);
    text.addVerifyListener(new VerifyListener()
    {
      @Override
      public void verifyText(VerifyEvent e)
      {
        // check that only numerical values are entered
        char[] chars = e.text.toCharArray();
        for (char c : chars)
        {
          if (!Character.isDigit(c))
          {
            e.doit = false;
            return;
          }
          // check that no time part greater than 59 is entered
          if (text.getText() != null && text.getText().length() >= 1
              && text.getText().charAt(0) >= '6')
          {
            e.doit = false;
            return;
          }
        }
      }
    });

    return text;
  }

  private void addNextWidget(final Text text, final Control nextControl)
  {
    text.addModifyListener(new ModifyListener()
    {
      @Override
      public void modifyText(ModifyEvent e)
      {
        // Switches focus to seconds field when minutes have been entered
        // (or from seconds to enter button when secondes hae been entered)

        if (// 
        // switch focus when two digits have been entered
        (text.getText().length() == 2)//
            || //
            // switch focus when one digit >= 6 has been entered
            (//
            text.getText().length() == 1 //
            && text.getText().charAt(0) >= '6' //
            ) //
        )
        {
          nextControl.setFocus();
        }
      }
    });
  }

  private void togglePauseFromButton()
  {
    // calls togglePauseInternal on this in turn
    this.behaviourDelegate.togglePause();
  }

  void togglePauseInternal()
  {
    if (this.behaviourDelegate.isPaused())
    {
      this.compositeMainArea.setVisible(false);
      this.textBigClock.setVisible(false);
      this.buttonPause.setText("Continue");
    }
    else
    {
      this.compositeMainArea.setVisible(true);
      this.textBigClock.setVisible(true);
      this.buttonPause.setText("Pause");
    }
  }

  void dispose()
  {
    this.fontBigClock.dispose();
  }
}
